﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using System.Threading;
using System.Diagnostics;

namespace AutoUpdate
{
	public partial class Form1 : Form
	{
		delegate void SetTextCallback(string text);
		Thread msgThread = null;

		List<string> ignoreFile = new List<string>();
		List<string> ignoreDirectory = new List<string>();
		List<string> updateFile = new List<string>();
		public Form1()
		{
			InitializeComponent();

			ClientAppPath = System.Configuration.ConfigurationSettings.AppSettings["ClientAppPath"].ToString();
			ServerConfigFilePath = System.Configuration.ConfigurationSettings.AppSettings["ServerPath"].ToString() + @"AutoUpdate.xml";
			ServerDownloadURL = System.Configuration.ConfigurationSettings.AppSettings["ServerPath"].ToString();
			CurrentConfigSavePath = Path.GetTempPath() + @"\\AutoUpdate.xml";
			ServerAppTempPath = Path.GetTempPath();
			AppFolder = System.Configuration.ConfigurationSettings.AppSettings["AppFolder"].ToString();
		}

		private void ReadConfig()
		{

			#region 更新程序初始化
			msgThread = new Thread(new ParameterizedThreadStart(ShowMsg));
			msgThread.Start("更新程序初始化！");
			#endregion
			//打开xml文件 
			FileStream myFile = new FileStream(CurrentConfigSavePath, FileMode.Open);
			//xml文件阅读器 
			XmlTextReader xml = new XmlTextReader(myFile);
			while (xml.Read())
			{
				if (xml.Name == "IgnoreFile")
				{
					ignoreFile.Add(xml.GetAttribute("FileName"));
				}
				if (xml.Name == "IgnoreFolder")
				{
					if (xml.GetAttribute("Folder") != null)
					{
						ignoreDirectory.Add(xml.GetAttribute("Folder"));
					}
				}
				if (xml.Name == "UpdateFile")
				{
					updateFile.Add(xml.GetAttribute("FileName"));
				}
			}
			xml.Close();
			myFile.Close();

		}
		#region 路径 属性
		/// <summary>
		/// 下载服务器端配置文件临时路径
		/// </summary>
		public string CurrentConfigSavePath { get; set; }

		/// <summary>
		/// 服务器配置文件路径
		/// </summary>
		public string ServerConfigFilePath { get; set; }

		/// <summary>
		/// 当前客户端应用程序路径
		/// </summary>
		public string ClientAppPath { get; set; }

		/// <summary>
		/// 服务器程序包下载路径
		/// </summary>
		public string ServerDownloadURL { get; set; }

		/// <summary>
		/// 程序包临时存放路径
		/// </summary>
		public string ServerAppTempPath { get; set; }

		/// <summary>
		/// 应用程序文件夹
		/// </summary>
		public string AppFolder { get; set; }
		#endregion

		#region overload formLoad event
		/// <summary>
		/// 关闭报账程序主进程
		/// </summary>
		protected override void OnLoad(EventArgs e)
		{
			System.Diagnostics.Process[] pros = System.Diagnostics.Process.GetProcessesByName("MCClient.exe");
			foreach (System.Diagnostics.Process item in pros)
			{
				item.Kill();
			}
			base.OnLoad(e);
		}
		/// <summary>
		/// 更新成功后启动报账程序进程
		/// </summary>
		protected override void OnClosed(EventArgs e)
		{
			if (File.Exists(Application.StartupPath + "\\MCClient.exe"))
			{
				System.Diagnostics.Process.Start(Application.StartupPath + "\\MCClient.exe", "1");
				base.OnClosed(e);
			}
			// 关闭所有进程
			Process.GetCurrentProcess().Kill();
			Application.ExitThread();
			Application.Exit();
		}
		#endregion

		/// <summary>
		/// 更新开始
		/// </summary>
		private void UpdateGo()
		{
			if (!File.Exists(Application.StartupPath + @"\AutoUpdate.xml"))
			{
				msgThread = new Thread(new ParameterizedThreadStart(ShowMsg));
				msgThread.Start("获取原更新配置文件失败，请检查升级程序配置！");
				MessageBox.Show("获取原更新配置文件失败，请检查升级程序配置！", "错误！");
				Process.GetCurrentProcess().Kill();
				Application.Exit();
			}
			// 通过读取当前更新程序目录下的AutoComplete.xml读取当前应用程序的最后更新时间
			string clientUpdateDate = GetTheLastUpdateTime(Application.StartupPath + @"\AutoUpdate.xml");
			// 下载配置文件AutuComplate.xml到临时文件夹
			FTPDownload(CurrentConfigSavePath, ServerConfigFilePath);
			msgThread = new Thread(new ParameterizedThreadStart(ShowMsg));
			msgThread.Start("下载配置文件！");
			// 根据临时文件获取服务器的更新日期
			string serverUpdateDate = GetTheLastUpdateTime(CurrentConfigSavePath);
			if (!string.IsNullOrEmpty(clientUpdateDate) && !string.IsNullOrEmpty(serverUpdateDate))
			{
				DateTime serverDate = new DateTime();
				DateTime clientDate = new DateTime();
				DateTime.TryParse(serverUpdateDate, out serverDate);
				DateTime.TryParse(clientUpdateDate, out clientDate);
				// 时间比较，如果服务器更新时间大于客户端应用程序更新时间，则下载更新
				if (serverDate > clientDate)
				{
					msgThread = new Thread(new ParameterizedThreadStart(ShowMsg));
					msgThread.Start("开始下载更新包......");
					ReadConfig();
					// 下载应用程序包
					if (!FTPDownloadFolder(ServerDownloadURL, AppFolder, ServerAppTempPath))
					{
						msgThread = new Thread(new ParameterizedThreadStart(ShowMsg));
						msgThread.Start("下载服务器程序包失败,升级程序无法继续！");
						MessageBox.Show("下载服务器程序包失败,升级程序无法继续", "错误！");
						return;
					}
					else
					{
						// 文件替换准备
						ReplaceFileReady();
						// 替换最新AutoUpdate.xml到本地程序文件夹中
						File.Delete(Application.StartupPath + @"\AutoUpdate.xml");
						File.Copy(CurrentConfigSavePath, Application.StartupPath + @"\AutoUpdate.xml");
						File.Delete(ClientAppPath + @"\AutoUpdate.xml");
						File.Copy(CurrentConfigSavePath, ClientAppPath + @"\AutoUpdate.xml");

						if (File.Exists(Application.StartupPath + "\\MCClient.exe"))
						{
							System.Diagnostics.Process.Start(Application.StartupPath + "\\MCClient.exe", "1");
						}
						Process.GetCurrentProcess().Kill();
					}
				}
				else
				{
					MessageBox.Show("当前程序版本已经是最新版本,无需更新", "提示！");
					if (File.Exists(Application.StartupPath + "\\MCClient.exe"))
					{
						System.Diagnostics.Process.Start(Application.StartupPath + "\\MCClient.exe", "1");
					}
					Process.GetCurrentProcess().Kill();
				}
			}
		}

		/// <summary>
		/// 文件替换准备,排除不更新的文件和忽略列表
		/// </summary>
		/// <returns></returns>
		private bool ReplaceFileReady()
		{
			DirectoryInfo tempDirectory = new DirectoryInfo(ServerAppTempPath);
			DirectoryInfo clientDirectory = new DirectoryInfo(ClientAppPath);

			#region 通过忽略文件列表全部更新
			msgThread = new Thread(new ParameterizedThreadStart(ShowMsg));
			msgThread.Start("更新本地文件....................................！");
			// 获取所有更新临时文件夹的文件
			GetSubDir(ServerAppTempPath + "App");
			// 排除不更新文件
			foreach (string item in ignoreFile)
			{
				arraylist.Remove(ServerAppTempPath + "App" + item);
			}
			// 排除忽略目录
			foreach (string item in ignoreDirectory)
			{
				List<string> list = arraylist.Where(t => t.Contains(ServerAppTempPath + "App" + item)).ToList();
				foreach (string s in list)
				{
					arraylist.Remove(s);
				}
			}
			List<string> updateFiles = new List<string>();
			foreach (string item in arraylist)
			{
				updateFiles.Add(item.Replace(ServerAppTempPath + "App", ""));
			}
			UpdateFile(updateFiles);
			msgThread = new Thread(new ParameterizedThreadStart(ShowMsg));
			msgThread.Start("版本更新成功！");
			MessageBox.Show("版本更新成功", "提示");
			//this.Close();
			#endregion

			return true;
		}

		/// <summary>
		/// 更新新版本临时文件到老版本文件
		/// </summary>
		/// <param name="updateFile">更新文件列表</param>
		private void UpdateFile(List<string> updateFile)
		{

			StringBuilder serverTemp = new StringBuilder();
			StringBuilder clientTemp = new StringBuilder();
			foreach (string item in updateFile)
			{
				// 读取新文件
				FileStream readStream = new FileStream(ServerAppTempPath + AppFolder + item, FileMode.Open);
				// 删除旧文件
				File.Delete(ClientAppPath + item);
				// 写入新文件
				//File.Create(ClientAppPath + item);
				FileStream writeStream = new FileStream(ClientAppPath + item, FileMode.OpenOrCreate);
				int bufferSize = 2048;
				int readCount;
				byte[] buffer = new byte[bufferSize];
				readCount = readStream.Read(buffer, 0, bufferSize);
				while (readCount > 0)
				{
					writeStream.Write(buffer, 0, readCount);
					readCount = readStream.Read(buffer, 0, bufferSize);
				}
				readStream.Close();
				writeStream.Close();
			}
		}

		/// <summary>
		/// 获取配置文件中程序最后更新日期
		/// </summary>
		/// <param name="Dir">文件所在目录</param>
		/// <returns></returns>
		private string GetTheLastUpdateTime(string Dir)
		{
			string LastUpdateTime = "";
			string AutoUpdaterFileName = Dir;
			if (!File.Exists(AutoUpdaterFileName))
				return LastUpdateTime;
			//打开xml文件 
			FileStream myFile = new FileStream(AutoUpdaterFileName, FileMode.Open);
			//xml文件阅读器 
			XmlTextReader xml = new XmlTextReader(myFile);
			while (xml.Read())
			{
				if (xml.Name == "UpdateTime")
				{
					//获取升级文档的最后一次更新日期 
					LastUpdateTime = xml.GetAttribute("Date");
					break;
				}
			}
			xml.Close();
			myFile.Close();
			return LastUpdateTime;
		}

		#region 读取文件列表递归
		private static List<string> arraylist = new List<string>();
		private static void GetSubDir(string s1)//递归获取文件夹中所有的文件名，并存入数组
		{
			string[] sDir = Directory.GetDirectories(s1);//获取子目录的名称
			string[] sFile = Directory.GetFiles(s1);//获取文件
			//foreach (var s in sFile)
			//{
			//    Console.WriteLine(s);
			//}
			for (int i = 0; i < sFile.Length; i++)
			{
				arraylist.Add(sFile[i]);   //将文件加入数组

			}
			for (int i = 0; i < sDir.Length; i++)
			{
				GetSubDir(sDir[i]);
			}
		}

		#endregion

		#region FTP
		/// <summary>  
		/// 单个文件下载方法  
		/// </summary>  
		/// <param name="localAddr">保存文件的本地路径</param>  
		/// <param name="FTPAddr">下载文件的FTP路径</param>   
		public int FTPDownload(string localAddr, string FTPAddr)
		{
			foreach (string item in ignoreDirectory)
			{
				if (FTPAddr.Contains(ServerDownloadURL + "App" + item.Replace('\\', '/')))
				{
					return 1;
				}
			}
			if (ignoreFile.Contains(FTPAddr.Replace(ServerDownloadURL + "App", "").Replace('/', '\\')))
			{
				return 1;
			}
			msgThread = new Thread(new ParameterizedThreadStart(ShowMsg));
			msgThread.Start("下载文件： " + FTPAddr);
			//FileMode常数确定如何打开或创建文件,指定操作系统应创建新文件。  
			//FileMode.Create如果文件已存在，它将被改写  
			FileStream outputStream = new FileStream(localAddr, FileMode.Create);
			FtpWebRequest downRequest = (FtpWebRequest)WebRequest.Create(new Uri(FTPAddr));

			//设置要发送到 FTP 服务器的命令
			downRequest.Method = WebRequestMethods.Ftp.DownloadFile;
			FtpWebResponse response = (FtpWebResponse)downRequest.GetResponse();
			Stream ftpStream = response.GetResponseStream();
			long cl = response.ContentLength;
			int bufferSize = 2048;
			int readCount;
			byte[] buffer = new byte[bufferSize];
			readCount = ftpStream.Read(buffer, 0, bufferSize);
			while (readCount > 0)
			{
				outputStream.Write(buffer, 0, readCount);
				readCount = ftpStream.Read(buffer, 0, bufferSize);
			}
			ftpStream.Close();
			outputStream.Close();
			response.Close();
			return 1;
		}

		/// <summary>
		/// 单个文件下载
		/// </summary>
		/// <param name="FTPAddress">FTP地址路径</param>  
		/// <param name="name">我们所选择的文件或者文件夹名字</param>  
		/// <param name="type">要发送到FTP服务器的命令</param>  
		/// <returns></returns>  
		public string[] FTPDownload(string FTPAddress, string name, string type)
		{
			WebResponse webresp = null;
			StreamReader ftpFileListReader = null;
			FtpWebRequest ftpRequest = null;
			try
			{
				ftpRequest = (FtpWebRequest)WebRequest.Create(new Uri(FTPAddress + name));
				ftpRequest.Method = type;
				webresp = ftpRequest.GetResponse();
				ftpFileListReader = new StreamReader(webresp.GetResponseStream(), Encoding.UTF8);
			}
			catch (Exception ex)
			{
				ex.ToString();
			}
			StringBuilder str = new StringBuilder();
			string line = ftpFileListReader.ReadLine();
			while (line != null)
			{
				str.Append(line);
				str.Append("\n");
				line = ftpFileListReader.ReadLine();
			}
			string[] fen = str.ToString().Split('\n');
			return fen;
		}

		/// 下载文件夹 
		/// </summary>  
		/// <param name="FTPAddress">FTP路径</param>  
		/// <param name="name">需要下载文件路径</param>  
		/// <param name="Myads">保存的本地路径</param>  
		public bool FTPDownloadFolder(string FTPAddress, string name, string Myads)
		{
			string downloadDir = Myads + name;
			string ftpdir = FTPAddress + name;
			string[] fullname = FTPDownload(FTPAddress, name, WebRequestMethods.Ftp.ListDirectoryDetails);
			//判断是否为单个文件   
			if (fullname.Length <= 2)
			{
				if (!Directory.Exists(downloadDir))
				{
					Directory.CreateDirectory(downloadDir);
				}
				if (fullname[fullname.Length - 1] == "")
				{
					string[] names = FTPDownload(FTPAddress, name, WebRequestMethods.Ftp.ListDirectory);
					FTPDownload(downloadDir + "/" + names[0], FTPAddress + name + "/" + names[0]);
				}
			}
			else
			{
				string[] onlyname = FTPDownload(FTPAddress, name, WebRequestMethods.Ftp.ListDirectory);
				if (!Directory.Exists(downloadDir))
				{
					Directory.CreateDirectory(downloadDir);
				}
				foreach (string names in fullname)
				{
					//判断是否具有文件夹标识<DIR>  
					if (names.Contains("<DIR>"))
					{
						string olname = names.Split(new string[] { "<DIR>" }, StringSplitOptions.None)[1].Trim();
						FTPDownloadFolder(ftpdir, "/" + olname, downloadDir);
					}
					else
					{
						foreach (string onlynames in onlyname)
						{
							if (onlynames == "" || onlynames == " " || names == "")
							{
								break;
							}
							else
							{
								if (names.Contains(" " + onlynames))
								{
									FTPDownload(downloadDir + "\\" + onlynames, FTPAddress + name + "/" + onlynames);
									break;
								}
							}
						}
					}
				}
			}
			return true;
		}

		#endregion

		private void Form1_Shown(object sender, EventArgs e)
		{
			Thread testThread = new Thread(new ThreadStart(TryFTPConnection));
			testThread.Start();
		}
		/// <summary>
		/// 测试FTP服务器连接
		/// </summary>
		private void TryFTPConnection()
		{
			try
			{
				FtpWebRequest testRequest = (FtpWebRequest)WebRequest.Create(new Uri(ServerDownloadURL));
				testRequest.Method = WebRequestMethods.Ftp.ListDirectory;
				testRequest.Timeout = 5000;
				WebResponse requestResult = testRequest.GetResponse();
				testRequest.Abort();
				Thread mainThread = new Thread(new ThreadStart(UpdateGo));
				mainThread.Start();
			}
			catch
			{
				msgThread = new Thread(new ParameterizedThreadStart(ShowMsg));
				msgThread.Start("FTP服务器长时间无响应，升级程序不能继续！");
				MessageBox.Show("FTP服务器长时间无响应，升级程序不能继续！");
				Process.GetCurrentProcess().Kill();
			}
		}

		/// <summary>
		/// 显示更新消息
		/// </summary>
		/// <param name="msg"></param>
		private void ShowMsg(object msg)
		{
			SetText(this.textBox1.Text + msg.ToString() + Environment.NewLine);
		}
		/// <summary>
		/// 显示消息文本到文本框
		/// </summary>
		/// <param name="text">消息内容</param>
		private void SetText(string text)
		{
			if (this.textBox1.InvokeRequired)
			{
				SetTextCallback d = new SetTextCallback(SetText);
				this.Invoke(d, new object[] { text });
			}
			else
			{
				this.textBox1.Text = text;
				this.textBox1.Focus();	// 获取焦点
				this.textBox1.Select(this.textBox1.TextLength, 0);	// 光标定位到文本最后
				this.textBox1.ScrollToCaret();	// 滚动到光标处
			}
		}
	}
}
